package cn.moon.protocol;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class ProxyMessageEncoder extends MessageToByteEncoder<ProxyMessage> {

    /**
     * 长度字段的占用的字节数
     */
    private final int lengthFieldLength;

    /**
     * 是否填充协议头
     */
    private final boolean prependHeader;

    /**
     * 要填充的协议头
     */
    private final byte[] header;

    public ProxyMessageEncoder(int lengthFieldLength) {

        this(lengthFieldLength, false, null);
    }


    public ProxyMessageEncoder(int lengthFieldLength, boolean prependHeader, byte[] header) {

        if (lengthFieldLength != 1 && lengthFieldLength != 2 &&
                lengthFieldLength != 3 && lengthFieldLength != 4 &&
                lengthFieldLength != 8) {
            throw new IllegalArgumentException(
                    "lengthFieldLength must be either 1, 2, 3, 4, or 8: " +
                            lengthFieldLength);
        }

        this.lengthFieldLength = lengthFieldLength;
        this.prependHeader = prependHeader;
        this.header = header;
    }


    public void prepend(ByteBuf out, int length){

        switch (lengthFieldLength) {
            case 1:
                if (length >= 256) {
                    throw new IllegalArgumentException(
                            "length does not fit into a byte: " + length);
                }
                out.writeByte((byte) length);
                break;
            case 2:
                if (length >= 65536) {
                    throw new IllegalArgumentException(
                            "length does not fit into a short integer: " + length);
                }
                out.writeShort((short) length);
                break;
            case 3:
                if (length >= 16777216) {
                    throw new IllegalArgumentException(
                            "length does not fit into a medium integer: " + length);
                }
                out.writeMedium(length);
                break;
            case 4:
                out.writeInt(length);
                break;
            case 8:
                out.writeLong(length);
                break;
            default:
                throw new Error("should not reach here");
        }
    }

    @Override
    protected void encode(ChannelHandlerContext ctx, ProxyMessage proxyMessage, ByteBuf out) {

        log.debug("======== ProxyMessageEncoder encode ========");

        if(prependHeader && proxyMessage.getType() == ProxyMessage.CONNECT){
            out.writeBytes(header);
        }

        // 消息类型长度= 消息类型长度(1) + 授权码长度占用(1) + 授权码长度 + 信道id长度占用(1) + 信道id长度 + 数据区长度占用(4) + 数据区长度
        int length = 1;

        // 授权码
        byte[] authorizationCode = proxyMessage.getAuthorizationCode().getBytes(CharsetUtil.UTF_8);
        length += Byte.BYTES + authorizationCode.length;

        // 信道id
        byte[] channelId = proxyMessage.getChannelId().getBytes(CharsetUtil.UTF_8);
        length += Byte.BYTES + channelId.length;

        // 数据区长度
        length += Integer.BYTES + proxyMessage.getData().length;

        prepend(out, length);

        // 消息类型
        out.writeByte(proxyMessage.getType());

        // 授权码
        out.writeByte(authorizationCode.length);
        out.writeBytes(authorizationCode);

        // 信道id
        out.writeByte(channelId.length);
        out.writeBytes(channelId);

        // 数据区
        out.writeInt(proxyMessage.getData().length);
        out.writeBytes(proxyMessage.getData());

    }
}